﻿import kliment.utils._String;
import kliment.geom.color.*;

/**
 * ...
 * @author	Kliment
 * @version	1.7.0
*/
class kliment.geom.color.Color {
	static public var RGB:Number =  1;
	static public var CMYK:Number = 2;
	static public var HSV:Number =  4;
	static public var LAB:Number =  8;
	static public var XYZ:Number =  16;
	
	private var _rgb:RGBColor;
	private var _hsv:HSVColor;
	private var _lab:LABColor;
	private var _cmyk:CMYKColor;
	private var _xyz:XYZColor;
	
	private var _lastChanged:Number = 0;
	private var _validSpaces:Number = 0;
	
	public var addListener:Function;
	public var removeListener:Function;
	private var broadcastMessage:Function;
	private var _listeners:Array;
	public var onChange:Function = function(target:Color):Void {}
	
	function Color(hex:Number) {
		AsBroadcaster(this);
		this.hex = hex;
	}
	
	public function clone():Color {
		var result:Color = new Color(hex);
		return result;
	}
	
	public function get hex():Number {
		if (!(_validSpaces & RGB)) _updateRGB();
		return _rgb.r << 16 | _rgb.g << 8 | _rgb.b;
	}
	
	public function set hex(hex:Number):Void {
		if (!_rgb) _rgb = new RGBColor(this);
		_rgb.setValue(hex >> 16 & 255, hex >> 8 & 255, hex & 255);
	}
	
	public function get hexSmart():Number {
		if (!(_validSpaces & RGB)) _updateRGB();
		return _round3P(_rgb.r, 17) << 16 | _round3P(_rgb.g, 17) << 8 | _round3P(_rgb.b, 17);
	}
	
	public function get hexSafe():Number {
		if (!(_validSpaces & RGB)) _updateRGB();
		return _round3P(_rgb.r, 51) << 16 | _round3P(_rgb.g, 51) << 8 | _round3P(_rgb.b, 51);
	}
	
	public function get hexString():String { 
		var result:String = hex.toString(16);
		return _String.addZero(result, 6);
	}
	public function get hexSmartString():String { return _String.addZero(hexSmart.toString(16), 6) }
	public function get hexSafeString():String { return _String.addZero(hexSafe.toString(16), 6) }
	
	public function set hexString(hexStr:String):Void {
		var val:Number = parseInt(hexStr, 16);
		if (!isNaN(val)) 
			hex = val;
	}
	
	public function get rgb():RGBColor { return _rgb }
	public function set rgb(value:RGBColor):Void {
		if (_rgb) _rgb.color = null;
		value.color = this;
		_rgb = value;
	}
	
	public function get hsv():HSVColor { 
		if (!(_validSpaces & HSV)) 
			_updateHSV(); 
		return _hsv;
	}
	public function set hsv(value:HSVColor):Void {
		if (_hsv) _hsv.color = null;
		value.color = this;
		_hsv = value;
	}
	
	public function get lab():LABColor { 
		if (!(_validSpaces & LAB)) 
			_updateLAB(); 
		return _lab;
	}
	public function set lab(value:LABColor):Void {
		if (_lab) _lab.color = null;
		value.color = this;
		_lab = value;
	}
	
	public function get cmyk():CMYKColor { 
		if (!(_validSpaces & CMYK)) 
			_updateCMYK();
		return _cmyk;
	}
	public function set cmyk(value:CMYKColor):Void {
		if (_cmyk) _cmyk.color = null;
		value.color = this;
		_cmyk = value;
	}
	
	public function get xyz():XYZColor { 
		if (!(_validSpaces & XYZ)) 
			_updateXYZ();
		return _xyz;
	}
	public function set xyz(value:XYZColor):Void {
		if (_xyz) _xyz.color = null;
		value.color = this;
		_xyz = value;
	}
	
	private function _updateHSV():Void {
		if (!(_validSpaces & RGB))
			_updateRGB();
		if (!_hsv)
			_hsv = new HSVColor(this);
		_rgb.toHSV(_hsv);
		_validSpaces = _validSpaces | HSV;
	}
	
	private function _updateXYZ():Void {
		if (!(_validSpaces & RGB))
			_updateRGB();
		if (!_xyz)
			_xyz = new XYZColor(this);
		_rgb.toXYZ(_xyz);
		_validSpaces = _validSpaces | XYZ;
	}
	
	private function _updateCMYK():Void{
		if (!(_validSpaces & RGB))
			_updateRGB();
		if (!_cmyk)
			_cmyk = new CMYKColor(this);
		_rgb.toCMYK(_cmyk);
		_validSpaces = _validSpaces | CMYK;
	}
	
	private function _updateLAB() : Void{
		if (!(_validSpaces & XYZ)) 
			_updateXYZ();
		if (!_lab)
			_lab = new LABColor(this);
		_lab = _xyz.toLAB(_lab);
		_validSpaces = _validSpaces | LAB;
	}
	
	private function _updateRGB():Void{
		if (!_rgb) 
			_rgb = new RGBColor(this);
		
		switch(_lastChanged){
			case CMYK:
				_updateRGBfromCMYK();
			break;
			case HSV:
				_updateRGBfromHSV();
			break;
			case LAB:
				_updateRGBfromLAB();
			break;
			case XYZ:
				_updateRGBfromXYZ();
			break;
		}
		_validSpaces = _validSpaces | RGB;
	}
	
	private function _updateRGBfromCMYK():Void {
		_cmyk.toRGB(_rgb);
	}
	
	private function _updateRGBfromHSV():Void {
		hsv.toRGB(_rgb);
	}
	
	private function _updateRGBfromLAB():Void {
		if (!_xyz)
			_xyz = new XYZColor(this);
		
		_lab.toXYZColor(_xyz);
		_updateRGBfromXYZ();
	}
	
	private function _updateRGBfromXYZ():Void {
		_xyz.toRGB(_rgb)
	}
	
	public function valueChanged(value:Number):Void{
		_validSpaces = value;
		_lastChanged = value;
		
		onChange(this);
		broadcastMessage('onChange', this);
	}
	
	private function _round3P(color:Number, divisor:Number):Number {
		return Math.round(color / divisor) * divisor;
	}
	
}